home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Interactive Web Graphics with Shout 3D
/
Interactive Web Graphics With Shout 3D.iso
/
mac
/
Shout3Ddemo
/
S3D_2E1.exe
/
Shout3d_runtime
/
codebase
/
applets
/
ModDunkPanel.java
< prev
next >
Wrap
Text File
|
2000-09-01
|
31KB
|
966 lines
/**
Company: Eyematic Interfaces
Project: Shout3D 2.0 Sample Code
Class: ModDunkPanel
Date: April 26, 1999
Description: Class for Panel that displays Mod Dunk Game
(C) Copyright Eyematic Interfaces, Inc. - 1997-2000 - All rights reserved
*/
package applets;
import java.applet.*;
import java.awt.*;
import java.util.Hashtable;
import java.io.*;
import java.util.Date;
import java.net.URL;
import shout3d.*;
import shout3d.math.*;
import shout3d.core.*;
/**
* ModDunk Game panel
*
* A little game.
* Click the mouse down --> Ward Hole will pull his arm back
* Release the mouse --> Ward Hole will let fly the plunger
*
* The object is to time the release so that the plunger hits the
* target. If it does, an animation is played in which Jo falls into
* the dunk tank.
*
* (Note: Cheaters can use the right mouse button to always win)
*
* @author Paul Isaacs
* @author Jim Stewartson
* @author Dave Westwood
*/
public class ModDunkPanel extends Shout3DPanel implements DeviceObserver {
//{{ Shout3DApplet methods
/**
* Constructor
*/
public ModDunkPanel(Shout3DApplet applet){
super(applet);
}
// Pointers to handy nodes
//
Transform throwingArm; // USE Warh_Arm1_L
Interpolator warh_Arm1_R_Interp; // interpolator for Warh_Arm1_R
Interpolator warh_Arm2_R_Interp; // interpolator for Warh_Arm2_R
Transform plungerRoot; // USE PLUNGER_ROOT
Transform stuntPlunger; // USE STUNT_PLUNGER
Transform target; // Use Prop_Target
Transform targetMover; // Use Prop_Target_Mover
// Interpolators for jo
Interpolator jo_Hip_PosInterp;
Interpolator jo_Hip_RotInterp;
Interpolator jo_Leg2_L_RotInterp;
Interpolator jo_Leg2_R_RotInterp;
Interpolator jo_Chest_RotInterp;
Interpolator jo_Arm1_L_RotInterp;
Interpolator jo_Arm1_R_RotInterp;
Interpolator prop_DivingBoard_RotInterp; //interpolator for Prop_DivingBoard
Interpolator prop_DivingBoard_PosInterp; //interpolator for Prop_DivingBoard
// Paths to handy paths
Node stuntPlungerPath[];
/**
*
* This method is automatcially called by the parent class Shout3DPanel
* at the correct time during initialize().
*
* Subclasses should implement this to perform any custom initialization tasks.
*/
public void customInitialize() {
// Load the audio for the gong.
String gongSound = applet.getParameter("gongSound");
if (gongSound != null){
gong = applet.getAudioClip(applet.getCodeBase(), gongSound);
}
// To make it easy to find interpolators that effect specific
// nodes, collect them all into a handy list
collectInterpolators();
// Disconnect output of all time sensors.
// Instead, we'll control the fractions of all interpolators
// through the simulation.
disconnectAllTimeSensors();
Node testNode;
// Get pointers to interesting nodes
// WARD HOLE character:
//
// Left Upper Arm, to rotate using physics
if ((throwingArm = (Transform) getNodeByName("Warh_Arm1_L")) == null)
throw new Shout3DException("could not find node named throwingArm");
//
// Right Arm interpolators, to be controlled by simulation.
if ((testNode = (Transform) getNodeByName("Warh_Arm1_R")) == null)
throw new Shout3DException("could not find node named Warh_Arm1_R");
if ((warh_Arm1_R_Interp = findOrientationInterpolator(testNode)) == null)
throw new Shout3DException("could not find node named Arm1_R_Interp");
if ((testNode = (Transform) getNodeByName("Warh_Arm2_R")) == null)
throw new Shout3DException("could not find node named Warh_Arm2_R");
if ((warh_Arm2_R_Interp = findOrientationInterpolator(testNode)) == null)
throw new Shout3DException("could not find node named warh_Arm2_R_Interp");
// PLUNGER
//
// Transform to use in placing the plunger
if ((plungerRoot = (Transform) getNodeByName("PLUNGER_ROOT")) == null)
throw new Shout3DException("could not find node named PLUNGER_ROOT");
//
// This keeps track of where the plungerRoot should be
// placed during the WAITING, WINDUP, and THROWING stages
if ((stuntPlunger = (Transform) getNodeByName("STUNT_PLUNGER")) == null)
throw new Shout3DException("could not find node named STUNT_PLUNGER");
// Get path to the stunt plunger, which will be required to
// find transform between worldspace and the plunger
// root node is stuntPlungerPath[0] and
// stuntPlunger is stuntPlungerPath[stuntPlungerPath.length-1]
Searcher mySearcher = getNewSearcher();
mySearcher.setNode(stuntPlunger);
stuntPlungerPath = mySearcher.searchFirst(getScene());
// JO character
//
// Find the interpolators affected various nodes in Jo's body
if ((testNode = (Transform)getNodeByName("Jo_Hip")) == null)
throw new Shout3DException("could not find node named Jo_Hip");
jo_Hip_PosInterp = findPositionInterpolator(testNode);
jo_Hip_RotInterp = findOrientationInterpolator(testNode);
if ((testNode = (Transform)getNodeByName("Jo_Leg2_L")) == null)
throw new Shout3DException("could not find node named Jo_Leg2_L");
jo_Leg2_L_RotInterp = findOrientationInterpolator(testNode);
if ((testNode = (Transform)getNodeByName("Jo_Leg2_R")) == null)
throw new Shout3DException("could not find node named Jo_Leg2_R");
jo_Leg2_R_RotInterp = findOrientationInterpolator(testNode);
if ((testNode = (Transform)getNodeByName("Jo_Chest")) == null)
throw new Shout3DException("could not find node named Jo_Chest");
jo_Chest_RotInterp = findOrientationInterpolator(testNode);
if ((testNode = (Transform)getNodeByName("Jo_Arm1_L")) == null)
throw new Shout3DException("could not find node named Jo_Arm1_L");
jo_Arm1_L_RotInterp = findOrientationInterpolator(testNode);
if ((testNode = (Transform)getNodeByName("Jo_Arm1_R")) == null)
throw new Shout3DException("could not find node named Jo_Arm1_R");
jo_Arm1_R_RotInterp = findOrientationInterpolator(testNode);
// DIVING BOARD
//
if ((testNode = (Transform) getNodeByName("Prop_DivingBoard")) == null)
throw new Shout3DException("could not find node named Prop_DivingBoard");
prop_DivingBoard_RotInterp = findOrientationInterpolator(testNode);
prop_DivingBoard_PosInterp = findPositionInterpolator(testNode);
// TARGET
//
if ((target = (Transform) getNodeByName("Prop_Target")) == null)
throw new Shout3DException("could not find node named Prop_Target");
if ((targetMover = (Transform) getNodeByName("Prop_Target_Mover")) == null)
throw new Shout3DException("could not find node named Prop_Target_Mover");
// Calculate center of target
calculateTargetCenter();
// Register to receive deviceInput
// Argument "DeviceInput" means to watch for all devices
addDeviceObserver(this, "DeviceInput", null);
getRenderer().addRenderObserver(this, null);
}
/**
* Finalize
*/
protected void finalize() throws Throwable {
myInterpolators = null;
removeDeviceObserver(this,"DeviceInput");
super.finalize();
}
//}} Shout3DApplet methods
//{{ DeviceObserver methods
/**
* When mouse goes DOWN, Ward Hole's arm starts to move back.
* When mouse goes UP, the arm is released.
*
* Pressing the 't' or 'T' key toggles the target's swaying motion.
*/
public boolean onDeviceInput(DeviceInput di, Object userData) {
if (di.isOfType("MouseInput")) {
MouseInput mi = (MouseInput) di;
switch(mi.which) {
case MouseInput.DOWN: return onMouseDown(mi.x, mi.y, mi.button);
case MouseInput.UP: return onMouseUp(mi.x, mi.y, mi.button);
}
}
else if (di.isOfType("KeyboardInput")) {
KeyboardInput ki = (KeyboardInput) di;
if (ki.which == KeyboardInput.PRESS) {
return onKeyDown(ki.key);
}
}
return false;
}
//}} Device response methods
/////////////////////////////////////////////////////////
// Mod Dunk Game Specific Methods Begins here
/**
* Mouse goes down
*
* @param x the x position of the mouse down event
* @param y the y position of the mouse down event
*/
public boolean onMouseDown(int x, int y, int whichButton){
// A click always pulls back the arm.
// Release is timed to occur at releaseTime after the
// release.
pullBackArm();
// Pulling back with right mouse lets you win.
alwaysAWinner = (whichButton == 1);
// This panel always does something on mouse down...
return true;
}
/**
* Mouse goes up.
*
* @param x the x position of the mouse down event
* @param y the y position of the mouse down event
*/
public boolean onMouseUp(int x, int y, int whichButton){
// release the arm.
releaseArm();
return true;
}
/**
* Key goes down.
*
* @param key the key
*/
public boolean onKeyDown(int key){
// The 't' key toggles the target motion.
if (key == 't' || key == 'T'){
if (isTargetMoving)
setTargetMoving(false);
else
setTargetMoving(true);
}
// Don't want to stop anything additional from happening, return false
return false;
}
// When right mouse is used to throw, this is set to TRUE
// and jo always dunks:
boolean alwaysAWinner = false;
// State of the Thrower
//
static final int WAITING = 0;
static final int WINDUP = 1;
static final int THROWING = 2;
static final int AIRBORN = 3;
int curThrowerState = WAITING;
// State of poor little Jo's dunk
boolean isNowDunking = false;
float DUNK_DURATION = .666f;
// Throwing arm properties
//
float windupAcceleration = -10; // Rate at which windup occurs on mouse down
float throwCenterAngle = 4; // Resting angle of arm when not pulled
float throwSpringK = 35; // Spring constant that brings arm to center
float throwDamperK = 1; // Damping constant that affects arm
float minWindupAngle = 1; // Farthest that arm will retreat when mouse is pressed and held.
// Throwing arm state
//
// current values at any given time.
float throwArmAngle = 4;
float throwArmVelo = 0;
float throwArmAccel = 0;
// Tuning this number makes the arm go faster/slower when released.
static final float PLUNGER_VELO_HACK_FACTOR = 1.2f; //1.5f; //2.0f;
// Plunger state
float plungerPos[] = { 0f, 0f, 0f };
float plungerVelo[] = { 0f, 0f, 0f };
float plungerAcc[] = { 0f, -9.8f, 0f }; // gravity downward
float plungerRotXAngle = 0f; // For making 1D rotation easier.
float plungerAngVelo = 0;
float plungerAngAcc = 0;
boolean isPlungerTestedPastTarget = false;
// Target Properties
float targetCenter[] = { 0f, 0f, 0f };
float targetRadiusSquared = 0.75f * 0.75f;
float targetDepth = 0.1f;
boolean isTargetMoving = false;
double targetMotionStartTime = 0;
float targetRotOmega = 1;
float targetRotAmplitude = 1;
float targetRotPhase = 0;
// Time and timing
//
double prevTime = 0;
double curTime = 0;
float timeBetweenUpdates = 1;
float maxTimeStep = 0.1f;
double dunkStartTime = 0;
double startThrowTime = 0;
static final float THROW_TO_RELEASE_TIME = .26f; //.3f;
// Sound
AudioClip gong;
/**
* This is called once per frame, and updates everything
* based on what's happened so far.
*/
void updateGame() {
// Animate the target
if (isTargetMoving)
moveTarget();
// Simulates motion of arm using physics.
// If AIRBORN, will also simulate trajectory of plunger.
// The result is a set of positions and orientations that
// must then be put into the scene graph in the rest of this
// method
performSimulation();
// Sets the rotation of the throwing shoulder based on
// rotation calculated in performSimulation.
setThrowingArmRotation();
// Right arm is moved by setting the fraction of an
// animation so that it is coordinated with the throwing arm's motion.
animateFollowerArm();
if (curThrowerState != AIRBORN) {
// Makes the plunger follow the stuntPlunger, a dummy transform
// at the end of Ward Hole's arm.
makePlungerHeldByWarhol();
}
else {
// Places the plunger based on simulated trajectory
// calculated in performSimulation()
makePlungerFollowTrajectory();
}
// Is it time to switch from THROWING to AIRBORN?
if (curThrowerState == THROWING) {
if ((curTime - startThrowTime) > THROW_TO_RELEASE_TIME)
releasePlunger();
}
// If this is the frame where the target was hit, start the dunking!
if (didPlungerHitTarget()) {
startDunk();
// This makes the plunger bounce backwards
plungerPos[2] = targetCenter[2];
plungerVelo[2] *= -0.5;
plungerAngVelo *= -0.5;
// After the first target hit, the
// target starts animating:
if (isTargetMoving == false)
setTargetMoving(true);
}
if (isNowDunking) {
animateDunk();
}
}
/**
* Toggles whether the target is swaying back and forth.
*/
public void setTargetMoving(boolean onOff) {
isTargetMoving = onOff;
if (onOff) {
targetMotionStartTime = getClock().getAbsoluteTime();
}
else {
// remember the phase as the time we're at now, so if
// target is restarted, things will pick up nicely.
float rotTime = (float)(targetRotPhase + (curTime - targetMotionStartTime));
targetRotPhase = rotTime;
}
}
/**
* Called when the target is moving to advance it based on
* a sin function and the time passed since it started moving
*/
void moveTarget() {
float rotTime = (float)(targetRotPhase + (curTime - targetMotionStartTime));
float targetAngle = (float) (targetRotAmplitude * Math.sin( targetRotOmega * rotTime));
// load axis/angle for rotation about z of targetAngle
targetMover.rotation.getValue()[0] = 0;
targetMover.rotation.getValue()[1] = 0;
targetMover.rotation.getValue()[2] = 1;
targetMover.rotation.getValue()[3] = targetAngle;
//notify
targetMover.rotation.setValue(targetMover.rotation.getValue());
}
/** Simulates motion of arm using physics.
* If AIRBORN, will also simulate trajectory of plunger.
* The result is a set of positions and orientations that
* must then be put into the scene graph in the rest of this
* method
*/
void performSimulation()
{
// Save the time since last update. Other methods use this.
timeBetweenUpdates = (float)(curTime - prevTime);
// Execute simulation in small time steps until up to current time.
// Required since one large timestep can wreak havoc,
// especially when de-iconifying after a while.
float myTimeBetween = timeBetweenUpdates;
while ( myTimeBetween > 0 ) {
if ( myTimeBetween < maxTimeStep )
performSimulationStep(myTimeBetween);
else
performSimulationStep(maxTimeStep);
myTimeBetween = myTimeBetween - maxTimeStep;
}
}
/**
* Moves simulation forward in time by calculating an accelerations,
* then advancing the positions and velocities accordingly.
*
*/
void performSimulationStep(float deltaTime)
{
updateArmAcceleration();
doNumericalIntegration(deltaTime);
constrainThrowingArm();
constrainPlunger();
}
/**
* Depending on the state of the arm, different accelerations are used.
*/
void updateArmAcceleration()
{
if (curThrowerState == WAITING) {
// do nothing
}
else if (curThrowerState == WINDUP) {
// rotate backwards by predetermined acceleration.
throwArmAccel = windupAcceleration;
}
else if (curThrowerState == THROWING ||
curThrowerState == AIRBORN) {
// Calculate acceleration based on standard simple spring/damper equation.
throwArmAccel = - throwSpringK * (throwArmAngle - throwCenterAngle)
- throwDamperK * throwArmVelo;
}
}
/**
* Given starting position/velocity/acceleration, find the position
* and velocity at the end of the time step.
*
* Note to anyone who knows about Numerical Integration:
* This is just plain Euler integration, error prone for
* 'stiff' simulations. This simulation is set up to
* not be stiff, though.
*
*/
void doNumericalIntegration(float deltaTime)
{
// Calculate new angular velocity and angle for the throwing arm.
throwArmVelo = throwArmVelo + throwArmAccel * deltaTime;
throwArmAngle = throwArmAngle + throwArmVelo * deltaTime;
// Only make the plunger fly if AIRBORN, else it is
// positioned during makePlungerHeldByWarhol
if (curThrowerState == AIRBORN) {
// translation
plungerVelo[0] = plungerVelo[0] + plungerAcc[0] * deltaTime;
plungerVelo[1] = plungerVelo[1] + plungerAcc[1] * deltaTime;
plungerVelo[2] = plungerVelo[2] + plungerAcc[2] * deltaTime;
plungerPos[0] = plungerPos[0] + plungerVelo[0] * deltaTime;
plungerPos[1] = plungerPos[1] + plungerVelo[1] * deltaTime;
plungerPos[2] = plungerPos[2] + plungerVelo[2] * deltaTime;
// rotation
plungerAngVelo = plungerAngVelo + plungerAngAcc * deltaTime;
plungerRotXAngle = plungerRotXAngle + plungerAngVelo * deltaTime;
}
}
/**
* The throwing arm is constrained not to rotate back further than minWindupAngle
*/
void constrainThrowingArm()
{
if (curThrowerState == WINDUP) {
// constrain arm not to go too far back
if (throwArmAngle < minWindupAngle) {
throwArmAngle = minWindupAngle;
// if forcing a stop, immediately decelerate
throwArmVelo = 0;
}
}
}
// The boundaries of the playing area.
// These are hardcoded based on knowledge of the game.
float LEFT_WALL_X = -5;
float RIGHT_WALL_X = 7;
float NEAR_WALL_Z = -5;
float FAR_WALL_Z = 6;
/**
* The plungers motion is reflected and damped whenever it hits
* a wall, the floor, or the target.
*/
void constrainPlunger()
{
if (curThrowerState != AIRBORN)
return;
boolean flipAngVelo = false;
if (plungerPos[1] < 0.0f) {
// Plunger slipped under the floor.
// Translation:
//
// Move up to floor level, reflect and damp the vertical velocity.
plungerPos[1] = 0.0f;
plungerVelo[1] = plungerVelo[1] * -.5f;
//
// when hitting the floor, friction damps sideways
plungerVelo[0] = plungerVelo[0] * .95f;
plungerVelo[2] = plungerVelo[2] * .95f;
// Rotation:
// when hitting the floor, flip and dampen angVelo
plungerAngVelo *= -.5f;
//
// If plunger is now spinning slowly, start leveling it out
// a bit:
if (Math.abs(plungerAngVelo) < .5) {
// First, normalize angle between -PI and +PI
while(plungerRotXAngle > 3.1416)
plungerRotXAngle -= 6.2832;
while(plungerRotXAngle < -3.1416)
plungerRotXAngle += 6.2832;
// Now, work like a gentle spring towards horizontal.
if (plungerRotXAngle > 0f) {
plungerAngVelo -= 1 * (plungerRotXAngle - 1.57079);
}
else {
plungerAngVelo -= 1 * (plungerRotXAngle + 1.57079);
}
}
}
// If a wall is hit, just reflect and dampen the translational velocity
//
if (plungerPos[0] < LEFT_WALL_X) {
plungerPos[0] = LEFT_WALL_X;
plungerVelo[0] = plungerVelo[0] * -.5f;
// no change in angular velo since flight || to x-walls
}
if (plungerPos[0] > RIGHT_WALL_X) {
plungerPos[0] = RIGHT_WALL_X;
plungerVelo[0] = plungerVelo[0] * -.5f;
// no change in angular velo since flight || to x-walls
}
if (plungerPos[2] < NEAR_WALL_Z) {
plungerPos[2] = NEAR_WALL_Z;
plungerVelo[2] = plungerVelo[2] * -.5f;
flipAngVelo = true;
}
if (plungerPos[2] > FAR_WALL_Z) {
plungerPos[2] = FAR_WALL_Z;
plungerVelo[2] = plungerVelo[2] * -.5f;
flipAngVelo = true;
}
if (flipAngVelo) {
plungerAngVelo *= -.5f;
}
}
// These are hardcoded based on what looks good
float RIGHT_ARM_MIN_FRACTION = 0.25f;
float RIGHT_ARM_MAX_FRACTION = 1.0f;
/**
* Right arm is moved by setting the fraction of an
* animation so that it is coordinated with the throwing arm's motion.
*/
void animateFollowerArm()
{
// Ramps animation fraction between [RIGHT_ARM_MAX_FRACTION,RIGHT_ARM_MIN_FRACTION]
// to match the throwing arms' value in the range [throwCenterAngle,minWindupAngle]
float animationFraction = 0.0f;
if (throwArmAngle > throwCenterAngle) {
animationFraction = RIGHT_ARM_MIN_FRACTION;
}
else if (throwArmAngle < minWindupAngle) {
animationFraction = RIGHT_ARM_MAX_FRACTION;
}
else {
animationFraction = (throwArmAngle - throwCenterAngle)
/ (minWindupAngle - throwCenterAngle);
animationFraction = RIGHT_ARM_MAX_FRACTION * animationFraction + RIGHT_ARM_MIN_FRACTION;
}
// Set this fraction in animation of both joints of the right arm.
warh_Arm1_R_Interp.fraction.setValue(animationFraction);
warh_Arm2_R_Interp.fraction.setValue(animationFraction);
}
/**
* Sets the rotation of the throwing shoulder based on
* rotation calculated in performSimulation.
*/
void setThrowingArmRotation()
{
// Load rotation of throwArmAngle, about -x axis into throwingArm
throwingArm.rotation.getValue()[0] = -1;
throwingArm.rotation.getValue()[1] = 0;
throwingArm.rotation.getValue()[2] = 0;
throwingArm.rotation.getValue()[3] = throwArmAngle;
throwingArm.rotation.setValue(throwingArm.rotation.getValue());
}
/** Edits the plungerRoot (which lives in world space)
* so that it matches the location and rotation of
* stuntPlunger (which lives in the space at the end of warhol's
* arm)
*/
void makePlungerHeldByWarhol()
{
//Check this to avoid divide by 0
//Shouldn't happen, but to be safe...
if (timeBetweenUpdates == 0f)
return;
// Convert the origin and y direction from
// stuntPlunger space to world space, matrix by matrix
//
Node curNode;
float p[] = { 0f, 0f, 0f };
float y[] = { 0f, 1f, 0f };
float mtx[];
for (int i = stuntPlungerPath.length - 1; i >=0; i--) {
if (stuntPlungerPath[i] instanceof Transform) {
mtx = ((Transform) stuntPlungerPath[i]).getMatrix();
MatUtil.multVecMatrix( mtx, p );
MatUtil.multDirMatrix( mtx, y );
}
}
// Set the translation from the transformed point
plungerRoot.translation.getValue()[0] = p[0];
plungerRoot.translation.getValue()[1] = p[1];
plungerRoot.translation.getValue()[2] = p[2];
//notify
plungerRoot.translation.setValue(plungerRoot.translation.getValue());
// Set the rotation as a rotation about the x axis, based on how the
// Calculate the X rotation required to rotate (0,1,0) into
// alignment with y
MatUtil.normalize(y);
// Get angle between worldspace y and transformed y.
float dotprod = y[1]; // Dot product with (0,1,0) is just y value.
plungerRotXAngle = (float) Math.acos(dotprod);
// Sign of z tells which direction:
if (y[2] < 0)
plungerRotXAngle *= -1;
// Now set the rotation field.
plungerRoot.rotation.getValue()[0] = 1;
plungerRoot.rotation.getValue()[1] = 0;
plungerRoot.rotation.getValue()[2] = 0;
plungerRoot.rotation.getValue()[3] = plungerRotXAngle;
plungerRoot.rotation.setValue(plungerRoot.rotation.getValue());
// Update the plunger velocities (translation and rotational)
// needed to move forward when the hand lets go of the plunger.
//
// Calculate based on deltaPos/deltaT, multiplied by a handy factor
// that lets us change the timing of the plunger so it's easier to
// control.
plungerVelo[0] = (p[0]-plungerPos[0]) * PLUNGER_VELO_HACK_FACTOR / timeBetweenUpdates;
plungerVelo[1] = (p[1]-plungerPos[1]) * PLUNGER_VELO_HACK_FACTOR / timeBetweenUpdates;
plungerVelo[2] = (p[2]-plungerPos[2]) * PLUNGER_VELO_HACK_FACTOR / timeBetweenUpdates;
// Match plunger angular velocity to that of arm
plungerAngVelo = -throwArmVelo;
// Save this to reflect the new position.
plungerPos[0] = p[0]; plungerPos[1] = p[1]; plungerPos[2] = p[2];
}
/**
* Places the plunger based on simulated trajectory
* calculated in performSimulation()
*/
void makePlungerFollowTrajectory(){
plungerRoot.translation.getValue()[0] = plungerPos[0];
plungerRoot.translation.getValue()[1] = plungerPos[1];
plungerRoot.translation.getValue()[2] = plungerPos[2];
//notify
plungerRoot.translation.setValue(plungerRoot.translation.getValue());
plungerRoot.rotation.getValue()[0] = 1;
plungerRoot.rotation.getValue()[1] = 0;
plungerRoot.rotation.getValue()[2] = 0;
plungerRoot.rotation.getValue()[3] = plungerRotXAngle;
//notify
plungerRoot.rotation.setValue(plungerRoot.rotation.getValue());
}
/**
* Calculates current worldspace location of target center.
* Since target may animate, this can be different each frame.
* This is done based on knowledge that there are only two
* transforms affecting the target, (target and targetMover)
*/
void calculateTargetCenter() {
// Start with 0,0,0 which is center of target,
targetCenter[0] = 0f;
targetCenter[1] = 0f;
targetCenter[2] = 0f;
// Transform by target's tranfsorm
MatUtil.multVecMatrix( target.getMatrix(), targetCenter);
// Transform by targetMover's transform
MatUtil.multVecMatrix( targetMover.getMatrix(), targetCenter);
}
/**
* Determines if the plunger hit the target during this last time step.
*/
boolean didPlungerHitTarget()
{
boolean result = false;
if (isPlungerTestedPastTarget == false) {
// Only compare if plunger has just past the depth of the target center.
// Target moves only in xy plane, so this is exactly when the
// test needs to be done.
if (plungerPos[2] < (targetCenter[2] + targetDepth)) {
if (alwaysAWinner) {
result = true;
}
else {
calculateTargetCenter();
// Is the plunger position within the targetRadius of the
// target's center?
float xdist = plungerPos[0] - targetCenter[0];
float ydist = plungerPos[1] - targetCenter[1];
float dist2d = xdist * xdist + ydist * ydist;
if ( dist2d < targetRadiusSquared ) {
result = true;
}
}
isPlungerTestedPastTarget = true;
}
}
return result;
}
/**
* Gets the dunk started
*/
void startDunk()
{
isNowDunking = true;
dunkStartTime = curTime;
if (gong != null) gong.play();
}
/**
* Determines the fraction to apply to all timers in the dunk animation.
*/
void animateDunk()
{
float fraction = (float)((curTime - dunkStartTime)/ DUNK_DURATION);
if (fraction > 1.0f)
fraction = 1.0f;
if (fraction < 0.0f)
fraction = 0.0f;
setDunkInterpolators(fraction);
}
/**
* Visits all dunk timers and sets to the given fraction.
*/
void setDunkInterpolators(float fraction)
{
jo_Hip_PosInterp.fraction.setValue(fraction);
jo_Hip_RotInterp.fraction.setValue(fraction);
jo_Leg2_L_RotInterp.fraction.setValue(fraction);
jo_Leg2_R_RotInterp.fraction.setValue(fraction);
jo_Chest_RotInterp.fraction.setValue(fraction);
jo_Arm1_L_RotInterp.fraction.setValue(fraction);
jo_Arm1_R_RotInterp.fraction.setValue(fraction);
prop_DivingBoard_RotInterp.fraction.setValue(fraction);
prop_DivingBoard_PosInterp.fraction.setValue(fraction);
}
/**
* Called when the mouse first clicks down.
*/
void pullBackArm()
{
// change state, initialize prevTime for this turn
curThrowerState = WINDUP;
// Reset Dunktank
isNowDunking = false;
setDunkInterpolators(0.0f);
}
/**
* Called when THROW_RELEASE_TIME has passed since the
* mouse was released.
*/
void releasePlunger()
{
// change state, advance prev time
curThrowerState = AIRBORN;
// Just released, so we'll want a new shot at target
isPlungerTestedPastTarget = false;
}
/**
* Called when the mouse is released.
* The plunger stays attached until
* THROW_RELEASE_TIME has passed, then releasePlunger()
* will be called during updateGame()
*/
void releaseArm()
{
curThrowerState = THROWING;
startThrowTime = curTime;
}
/**
*
* Called each frame. Updates time, then calles updateGame
*
* Note that no onPostRender() method is implemented in this class,
* even though it is required for all classes implementing RenderObserver.
* This is because the super class implements RenderObserver fully
* and onPostRender is inherited.
* Hence this class needs only to override the onPreRender() method,
* making sure to call super.onPreRender(r,userData) within the body
* of the method.
*
*/
public void onPreRender(Renderer r, Object userData){
super.onPreRender(r, userData);
boolean wasTimeZero = (curTime == 0);
prevTime = curTime;
curTime = getClock().getAbsoluteTime();
// System.out.println("wasTimeZero = " + wasTimeZero);
if ( !wasTimeZero ) {
updateGame();
}
}
Interpolator[] myInterpolators = null;
// Collects all the interpolators in the scene and puts the list into myInterpolators
void collectInterpolators() {
Searcher mySearcher = getNewSearcher();
mySearcher.setType("Interpolator");
Node[][] allPaths = mySearcher.searchAll(getScene());
if (allPaths == null)
return;
myInterpolators = new Interpolator[allPaths.length];
for (int i = 0; i < allPaths.length; i++) {
if (allPaths[i] == null)
myInterpolators[i] = null;
else
myInterpolators[i] = (Interpolator) allPaths[i][allPaths[i].length-1];
}
}
TimeSensor tmpSensor;
void disconnectAllTimeSensors() {
Searcher mySearcher = getNewSearcher();
mySearcher.setType("TimeSensor");
Node[][] allPaths = mySearcher.searchAll(getScene());
if (allPaths == null)
return;
for (int i = 0; i < allPaths.length; i++) {
tmpSensor = (TimeSensor) allPaths[i][allPaths[i].length-1];
for (int j = tmpSensor.fraction.getNumRoutes()-1; j >= 0; j--){
tmpSensor.fraction.deleteRoute(tmpSensor.fraction.getRoutedField(j));
}
}
}
// Returns interpolator that affects forNode.
// Second argument is whether or not to set the timeSensor of the
// interpolator to null
public Interpolator findOrientationInterpolator(Node forNode){
if ( !(forNode instanceof Transform))
return null;
Transform xf = (Transform) forNode;
for (int i = 0; i < myInterpolators.length;i++) {
if (myInterpolators[i] instanceof OrientationInterpolator){
if ( isRouted(((OrientationInterpolator)myInterpolators[i]).value, xf.rotation)){
return myInterpolators[i];
}
}
}
return null;
}
public Interpolator findPositionInterpolator(Node forNode){
if ( !(forNode instanceof Transform))
return null;
Transform xf = (Transform) forNode;
for (int i = 0; i < myInterpolators.length;i++) {
if (myInterpolators[i] instanceof PositionInterpolator){
if ( isRouted(((PositionInterpolator)myInterpolators[i]).value, xf.translation)){
return myInterpolators[i];
}
}
}
return null;
}
}